package com.mdd.front.service.goods.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.query.MPJQueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mdd.common.config.GlobalConfig;
import com.mdd.common.constant.GlobalConstant;
import com.mdd.common.core.PageResult;
import com.mdd.common.entity.goods.*;
import com.mdd.common.entity.image.GoodsImage;
import com.mdd.common.entity.salesman.SalesmanGoogs;
import com.mdd.common.entity.salesman.UserSalesman;
import com.mdd.common.enums.GoodsCategoryStatusEnum;
import com.mdd.common.exception.OperateException;
import com.mdd.common.mapper.category.GoodsCategoryMapper;
import com.mdd.common.mapper.goods.GoodsMapper;
import com.mdd.common.mapper.goods.LimitedTimeGoodsMapper;
import com.mdd.common.mapper.googs.SalesmanGoogsMapper;
import com.mdd.common.mapper.image.GoodsImageMapper;
import com.mdd.common.mapper.salesman.UserSalesmanMapper;
import com.mdd.common.mapper.specification.GoodsSpecificationMapper;
import com.mdd.common.mapper.unit.GoodsUnitMapper;
import com.mdd.common.utils.FormulaUtil;
import com.mdd.common.utils.RedisUtil;
import com.mdd.common.utils.TimeUtil;
import com.mdd.common.utils.UrlUtil;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.service.address.IUserAddressService;
import com.mdd.front.service.collect.IGoodsCollectService;
import com.mdd.front.service.comment.IGoodsCommentService;
import com.mdd.front.service.goods.IGoodsService;
import com.mdd.front.service.salesman.ISalesmanService;
import com.mdd.front.service.staff.IStaffService;
import com.mdd.front.validate.PageParam;
import com.mdd.front.vo.address.UserAddressDetailVo;
import com.mdd.front.vo.comment.GoodsCommentDetailVo;
import com.mdd.front.vo.goods.*;
import com.mdd.front.vo.specification.GoodsSpecificationDetailVo;
import com.mdd.front.vo.staff.StaffDetailVo;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 服务实现类
 */
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {

	@Resource
	GoodsMapper goodsMapper;

	@Resource
	private GoodsImageMapper goodsImageMapper;

	@Resource
	private GoodsCategoryMapper goodsCategoryMapper;

	@Resource
	private GoodsUnitMapper goodsUnitMapper;

	@Resource
	private IStaffService staffService;

	@Resource
	private IGoodsCommentService commentService;

	@Resource
	private IUserAddressService userAddressService;

	@Resource
	private IGoodsCollectService collectService;

	@Resource
	private GoodsSpecificationMapper goodsSpecificationMapper;

	@Resource
	private ISalesmanService iSalesmanService;
	@Resource
	private UserSalesmanMapper userSalesmanMapper;
	@Resource
	private SalesmanGoogsMapper salesmanGoogsMapper;

	@Resource
	private LimitedTimeGoodsMapper limitedTimeGoodsMapper;

	/**
	 * 服务列表
	 *
	 * @param pageParam 分页参数
	 * @param params    搜索参数
	 * @return PageResult<GoodsListVo>
	 */
	@Override
	public PageResult<GoodsListVo> list(PageParam pageParam, Map<String, String> params) {
		Integer page = pageParam.getPageNo();
		Integer limit = pageParam.getPageSize();
		List<Long> categoryIds = Lists.newArrayList();
		String categoryIdStr = params.get("categoryId");
		String name = params.get("name");
		if (StringUtils.isNotEmpty(categoryIdStr)) {
			categoryIds.add(Long.valueOf(categoryIdStr));
		}
		if (StringUtils.isNotEmpty(categoryIdStr)) {
			Long categoryPid = Long.valueOf(categoryIdStr);
			LambdaQueryWrapper<GoodsCategory> categoryQuery = new LambdaQueryWrapper<>();
			categoryQuery.eq(GoodsCategory::getPid, categoryPid);
			categoryQuery.eq(GoodsCategory::getIsShow, 1);
			List<GoodsCategory> categoryList = goodsCategoryMapper.selectList(categoryQuery);
			if (CollectionUtils.isNotEmpty(categoryList)) {
				categoryIds.addAll(categoryList.stream().map(GoodsCategory::getId).distinct().collect(Collectors.toList()));
			}
		}
		LambdaQueryWrapper<Goods> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(Goods::getIsDelete, GlobalConstant.NOT_DELETE);
		queryWrapper.eq(Goods::getStatus, GoodsCategoryStatusEnum.IN_SALES.getStatus());
		queryWrapper.in(CollectionUtils.isNotEmpty(categoryIds), Goods::getCategoryId, categoryIds);
		queryWrapper.like(StringUtils.isNotEmpty(name), Goods::getName, name);
		queryWrapper.orderByDesc(Goods::getSort);
		queryWrapper.orderByDesc(Goods::getId);
		String carousel = params.get("carousel");
		if(StringUtils.isNotEmpty(carousel)){
			queryWrapper.eq(Goods::getCarousel,carousel);
		}
		IPage<Goods> iPage = goodsMapper.selectPage(new Page<>(page, limit), queryWrapper);
		if (CollectionUtils.isEmpty(iPage.getRecords())) {
			return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), Lists.newArrayList());
		}
		Map<Long, String> categoryMap = this.getCategoryStr(iPage.getRecords());
		List<Long> unitIds = iPage.getRecords().stream().map(Goods::getUnitId).distinct().collect(Collectors.toList());
		Map<Long, String> unitMap = goodsUnitMapper.listByIds(unitIds).stream().collect(Collectors.toMap(item -> item.getId(), item -> item.getName()));
		List<GoodsListVo> list = new LinkedList<>();
		for (Goods item : iPage.getRecords()) {
			GoodsListVo vo = new GoodsListVo();
			BeanUtils.copyProperties(item, vo);
			vo.setCategory(categoryMap.get(item.getCategoryId()));
			vo.setUnit(unitMap.get(item.getUnitId()));
			vo.setImage(UrlUtil.toAbsoluteUrl(item.getImage()));
			vo.setStatusDesc(GoodsCategoryStatusEnum.getMap().get(vo.getStatus()));
			vo.setCreateTime(TimeUtil.timestampToDate(item.getCreateTime()));
			vo.setUpdateTime(TimeUtil.timestampToDate(item.getUpdateTime()));
			vo.setCarousel(item.getCarousel());
			list.add(vo);
		}
		return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), list);
	}

	@Override
	public StatisticsGoodsVo statistics(Map<String, String> params) {
		QueryWrapper<Goods> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("is_delete", 0);

		goodsMapper.setSearch(queryWrapper, params, new String[]{
				"like:name:str",
				"=:remarks:str",
				"=:categoryId@category_id:long",
				"=:unitId@unit_id:long",
				"=:image:str",
				"=:price:str",
				"=:scribingPrice@scribing_price:str",
				"=:status:int",
				"=:sort:int",
				"=:content:str",
				"=:orderNum@order_num:int",
		});
		List<Goods> goods = goodsMapper.selectList(queryWrapper);
		StatisticsGoodsVo statisticsGoodsVo = new StatisticsGoodsVo();
		if (CollectionUtils.isEmpty(goods)) {
			return statisticsGoodsVo;
		}
		long warehouseCount = goods.stream().map(Goods::getStatus).filter(item -> GoodsCategoryStatusEnum.WAREHOUSE.getStatus() == item).count();
		long inSalesCount = goods.stream().map(Goods::getStatus).filter(item -> GoodsCategoryStatusEnum.IN_SALES.getStatus() == item).count();
		statisticsGoodsVo.setAllCount(goods.size());
		statisticsGoodsVo.setInSalesCount((int) inSalesCount);
		statisticsGoodsVo.setWarehouseCount((int) warehouseCount);
		return statisticsGoodsVo;
	}


	private Map<Long, String> getCategoryStr(List<Goods> goodsList) {
		Map<Long, String> map = Maps.newHashMap();
		List<Long> categoryIds = goodsList.stream().map(Goods::getCategoryId).distinct().collect(Collectors.toList());
		List<GoodsCategory> categoryList = goodsCategoryMapper.selectBatchIds(categoryIds);
		List<Long> pidIds = categoryList.stream().map(GoodsCategory::getPid).filter(item -> item != null).distinct().collect(Collectors.toList());
		Map<Long, String> parentMap = goodsCategoryMapper.selectBatchIds(pidIds).stream().collect(Collectors.toMap(item -> item.getId(), item -> item.getName()));
		for (GoodsCategory goodsCategory : categoryList) {
			if (map.containsKey(goodsCategory.getId())) {
				continue;
			}
			StringBuilder sb = new StringBuilder();
			if (parentMap.containsKey(goodsCategory.getPid())) {
				sb.append(parentMap.get(goodsCategory.getPid())).append("/");
			}
			sb.append(goodsCategory.getName());
			map.put(goodsCategory.getId(), sb.toString());
		}
		return map;
	}

	/**
	 * 服务详情
	 *
	 * @param id 主键参数
	 * @return Goods
	 */
	@Override
	public GoodsDetailVo detail(Long id, Long userId, Long cityId) {
		Goods model = goodsMapper.selectOne(
				new QueryWrapper<Goods>()
						.eq("id", id)
						.eq("is_delete", 0)
						.last("limit 1"));
		Assert.notNull(model, "数据不存在");
		if (GoodsCategoryStatusEnum.WAREHOUSE.getStatus() == model.getStatus()) {
			throw new OperateException("服务已下架");
		}
		GoodsDetailVo vo = new GoodsDetailVo();
		BeanUtils.copyProperties(model, vo);
		vo.setImage(UrlUtil.toAbsoluteUrl(model.getImage()));
		// 轮播图
		LambdaQueryWrapper<GoodsImage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
		lambdaQueryWrapper.eq(GoodsImage::getGoodsId, model.getId());
		List<GoodsImage> imageList = goodsImageMapper.selectList(lambdaQueryWrapper);
		if (CollectionUtils.isNotEmpty(imageList)) {
			List<String> images = Lists.newArrayList();
			for (GoodsImage image : imageList) {
				images.add(UrlUtil.toAbsoluteUrl(image.getUri()));
			}
			vo.setGoodsImageList(images);
		}
		// 单位
		GoodsUnit goodsUnit = goodsUnitMapper.selectById(model.getUnitId());
		vo.setUnit(goodsUnit.getName());
		// 服务分类
		Map<Long, String> categoryMap = this.getCategoryStr(Lists.newArrayList(model));
		vo.setCategory(categoryMap.get(model.getCategoryId()));
		// 状态
		vo.setStatusDesc(GoodsCategoryStatusEnum.getMap().get(model.getStatus()));
		// 添加十个师傅
		if (cityId != null) {
			List<StaffDetailVo> staffList = staffService.listByCityId(cityId);
			staffList = staffList.stream().filter(item -> item.getStatus() == 1).distinct().collect(Collectors.toList());
			staffList = staffList.stream().filter(item -> item.getGoodsIds().contains(String.valueOf(model.getId()))).distinct().collect(Collectors.toList());
			vo.setStaffList(staffList);
		}
		// 添加一条最新的评论消息
		GoodsCommentDetailVo commentDetailVo = commentService.getLastCommentByGoodsId(model.getId());
		vo.setCommentDetailVo(commentDetailVo);
		// 是否收藏
		GoodsCollect goodsCollect = collectService.getByGoodsId(userId, id);
		if (goodsCollect != null) {
			vo.setCollectId(goodsCollect.getId());
			vo.setCollect(true);
		} else {
			vo.setCollect(false);
		}
		return vo;
	}

	@Override
	public List<GoodsDetailVo> detailByIds(List<Long> goodsIds) {
		List<GoodsDetailVo> list = Lists.newArrayList();
		if (CollectionUtils.isEmpty(goodsIds)) {
			return list;
		}
		for (Long goodsId : goodsIds) {
			list.add(this.detail(goodsId, null, null));
		}
		return list;
	}

	@Override
	public OrderGoodsDetailVo reserveGoods(Long id, Long userId, Integer number) {
		Goods model = goodsMapper.selectById(id);
		Assert.notNull(model, "数据不存在!");
		Integer orderNum = model.getOrderNum() == null ? 0 : model.getOrderNum();
		orderNum = orderNum + number;
		model.setOrderNum(orderNum);
		goodsMapper.updateById(model);

		GoodsUnit goodsUnit = goodsUnitMapper.selectById(model.getUnitId());
		// 构建订单详情
		OrderGoodsDetailVo vo = new OrderGoodsDetailVo();
		vo.setGoodsId(model.getId());
		vo.setGoodsName(model.getName());
		vo.setUnitName(goodsUnit.getName());
		vo.setGoodsNum(number);
		vo.setGoodsPrice(model.getPrice());
		vo.setTotalPrice(FormulaUtil.multiply(model.getPrice(), number, FormulaUtil.SCALE_TWO));
		vo.setTotalPayPrice(FormulaUtil.multiply(model.getPrice(), number, FormulaUtil.SCALE_TWO));
		// 用户地址
		UserAddressDetailVo addressDetailVo = userAddressService.getDefaultAddress(userId);
		vo.setUserAddressDetailVo(addressDetailVo);
		return vo;
	}

	@Override
	public OrderGoodsDetailVo detailByUserId(Long userId, Integer goodsNum, Long goodsId, Integer isBuy) {
		Goods model = iSalesmanService.getGoodsForSalesman(Math.toIntExact(goodsId),isBuy);
		BigDecimal productPrice  = null;
		//超过规格的选项的商品
		if (Objects.nonNull(model.getOrderNum()) && model.getOrderNum() > goodsNum) {
			new OperateException("请从规格里面选择，亲！");
		}
		Assert.notNull(model, "数据不存在!");
		GoodsUnit goodsUnit = goodsUnitMapper.selectById(model.getUnitId());

		// 获取是否有推销员价格
		if (Objects.nonNull(model.getId())){
			Integer salesmanId = userSalesmanMapper.selectOne(new LambdaQueryWrapper<UserSalesman>().eq(UserSalesman::getUserId, userId)).getSalesmanId();
			SalesmanGoogs salesmanGoogs = salesmanGoogsMapper.selectOne(new LambdaQueryWrapper<SalesmanGoogs>().eq(SalesmanGoogs::getSalesmanId, salesmanId).eq(SalesmanGoogs::getProductId, model.getId()));
			if (ObjectUtils.isNotEmpty(salesmanGoogs)){
				productPrice = salesmanGoogs.getProductPrice();
			}
		}

		// 构建订单详情
		OrderGoodsDetailVo vo = new OrderGoodsDetailVo();
		vo.setGoodsId(model.getId());
		vo.setImageUrl(UrlUtil.toAbsoluteUrl(model.getImage()));
		vo.setGoodsName(model.getName());
		vo.setUnitName(goodsUnit.getName());
		GoodsSpecification goods_specification_id = goodsSpecificationMapper.selectById(goodsId);
		if (goods_specification_id != null) {
			GoodsSpecificationDetailVo goodsSpecificationDetailVo = new GoodsSpecificationDetailVo();
			BeanUtils.copyProperties(goods_specification_id, goodsSpecificationDetailVo);
			if(ObjectUtils.isNotEmpty(productPrice)){
				model.setPrice(productPrice);
				goodsSpecificationDetailVo.setPrice(productPrice);
			}
			vo.setGoodsSpecificationDetailVo(goodsSpecificationDetailVo);
		}

		// 用户地址
		UserAddressDetailVo addressDetailVo = userAddressService.getDefaultAddress(userId);
		vo.setUserAddressDetailVo(addressDetailVo);
		vo.setGoodsPrice(model.getPrice());
		vo.setTotalPrice(FormulaUtil.multiply(model.getPrice(), goodsNum, FormulaUtil.SCALE_TWO));
		return vo;
	}

	@Override
	public void updateEntityById(Goods model) {
		super.updateById(model);
	}

	@Override
	public List<Goods> listByIds(List<Long> goodsIds) {
		if (CollectionUtils.isEmpty(goodsIds)) {
			return Lists.newArrayList();
		}
		List<Goods> list = super.listByIds(goodsIds);
		if (CollectionUtils.isEmpty(list)) {
			return Lists.newArrayList();
		}
		return list;
	}

	@Override
	public List<Goods> hotService(int limit) {
		MPJQueryWrapper<Goods> mpjQueryWrapper = new MPJQueryWrapper<>();
		mpjQueryWrapper.selectAll(Goods.class);
		mpjQueryWrapper.eq("t.is_delete", 0);
		mpjQueryWrapper.eq("gc.is_show", 1);
		mpjQueryWrapper.innerJoin("?_goods_category gc ON gc.id = t.category_id".replace("?_", GlobalConfig.tablePrefix));
		mpjQueryWrapper.orderByDesc("t.order_num");
		mpjQueryWrapper.orderByDesc("t.create_time");
		mpjQueryWrapper.last("limit " + limit);
		List<Goods> goods = goodsMapper.selectList(mpjQueryWrapper);
		System.out.println("goods = " + goods);
		return goods;
	}

	@Override
	public Goods getEntityById(Long goodsId) {
		return super.getById(goodsId);
	}

	/**
	 * @param pageParam
	 * @return
	 */
	@Override
	public PageResult<GoodsListVo> hotList(PageParam pageParam) {
		String redisKey = pageParam.getPageNo() + "=" + pageParam.getPageSize();
		if (RedisUtil.exists(redisKey)) {
			return (PageResult<GoodsListVo>) RedisUtil.get(redisKey);
		}
		ReentrantLock lock = new ReentrantLock();
		try {
			lock.tryLock(3, TimeUnit.SECONDS);
			LambdaQueryWrapper<Goods> queryWrapper = new LambdaQueryWrapper<>();
			queryWrapper.eq(Goods::getIsDelete, GlobalConstant.NOT_DELETE)
					.eq(Goods::getStatus, GlobalConstant.DELETE)
					.orderByDesc(Goods::getOrderNum);
			Page<Goods> page = new Page<>(pageParam.getPageNo(), pageParam.getPageSize());
			Page<Goods> goodsPage = goodsMapper.selectPage(page,
					queryWrapper);
			Map<Long, String> categoryMap = this.getCategoryStr(goodsPage.getRecords());
			List<Long> unitIds = goodsPage.getRecords().stream().map(Goods::getUnitId).distinct().collect(Collectors.toList());
			Map<Long, String> unitMap = goodsUnitMapper.listByIds(unitIds).stream().collect(Collectors.toMap(item -> item.getId(), item -> item.getName()));
			List<GoodsListVo> list = new LinkedList<>();
			for (Goods item : goodsPage.getRecords()) {
				GoodsListVo vo = new GoodsListVo();
				BeanUtils.copyProperties(item, vo);
				vo.setCategory(categoryMap.get(item.getCategoryId()));
				vo.setUnit(unitMap.get(item.getUnitId()));
				vo.setImage(UrlUtil.toAbsoluteUrl(item.getImage()));
				vo.setStatusDesc(GoodsCategoryStatusEnum.getMap().get(vo.getStatus()));
				vo.setCreateTime(TimeUtil.timestampToDate(item.getCreateTime()));
				vo.setUpdateTime(TimeUtil.timestampToDate(item.getUpdateTime()));
				list.add(vo);
			}
			PageResult<GoodsListVo> goodsListVoPageResult = PageResult.iPageHandle(goodsPage.getTotal(), goodsPage.getCurrent(), goodsPage.getSize(), list);
			RedisUtil.set(redisKey, goodsListVoPageResult, 60 * 60);
			return goodsListVoPageResult;
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		} finally {
			lock.unlock();
		}
	}

	/**
	 * @return
	 */
	@Override
	public GoodsDetailVo getTodaySpecials() {
		String key = "todaySpecials";
		DayOfWeek dayOfWeek = LocalDate.now(ZoneId.systemDefault()).getDayOfWeek();
		Integer o = (Integer) RedisUtil.hGet(key, String.valueOf(dayOfWeek.getValue()));
		if(Objects.isNull(o)) {
			return null;
		}
		Goods goods = goodsMapper.selectById(o);
		GoodsDetailVo goodsDetailVo = new GoodsDetailVo();
		BeanUtil.copyProperties(goods, goodsDetailVo);
		return goodsDetailVo;
	}

	/**
	 * 限时商品列表
	 *
	 * @param pageParam 分页参数
	 * @param params    搜索参数
	 * @return PageResult<LimitedTimeGoodsVo>
	 */
	@Override
	public PageResult<LimitedTimeGoodsListVo> listLimited(PageParam pageParam, Map<String, String> params) {
		Integer page  = pageParam.getPageNo();
		Integer limit = pageParam.getPageSize();

		QueryWrapper<LimitedTimeGoods> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("is_delete", 0);
		queryWrapper.orderByDesc("id");

		this.limitedTimeGoodsMapper.setSearch(queryWrapper, params, new String[]{
				"=:goodsId@goods_id:int",
				"=:price:str",
				"=:limitedGoodsNum@limited_goods_num:int",
				"=:limitedStartTime@limited_start_time:int",
				"=:limitedEndTime@limited_end_time:int",
		});

		IPage<LimitedTimeGoods> iPage = this.limitedTimeGoodsMapper.selectPage(new Page<>(page, limit), queryWrapper);

		List<LimitedTimeGoodsListVo> list = new LinkedList<>();
		for(LimitedTimeGoods item : iPage.getRecords()) {
			Goods goods = goodsMapper.selectById(item.getGoodsId());
			LimitedTimeGoodsListVo vo = new LimitedTimeGoodsListVo();
			BeanUtils.copyProperties(item, vo);
			vo.setCreateTime(TimeUtil.timestampToDate(item.getCreateTime()));
			vo.setUpdateTime(TimeUtil.timestampToDate(item.getUpdateTime()));
			vo.setGoodsName(goods.getName());
			vo.setBeforePrice(goods.getScribingPrice());
			vo.setImage(goods.getImage());
			list.add(vo);
		}
		return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), list);
	}
}
